# utils/scheduler_manager.py
import logging
import importlib
from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.date import DateTrigger
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR, EVENT_JOB_MISSED
from django.conf import settings
from django.utils import timezone

from app.models import ScheduledTask, TaskExecutionLog, TaskMetrics
from .task_executor import AsyncTaskExecutor

logger = logging.getLogger(__name__)


class SchedulerManager:
    """APScheduler 调度器管理器"""
    
    _instance = None
    _scheduler = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        self._scheduler = None
        self._is_running = False
        self._task_executor = AsyncTaskExecutor()
        self._initialized = True
        logger.info("调度器管理器初始化完成")
    
    def start_scheduler(self):
        """启动调度器"""
        if self._scheduler is None:
            # 从 Django 设置中获取配置
            config = getattr(settings, 'SCHEDULER_CONFIG', {})
            
            self._scheduler = BackgroundScheduler()
            
            # 配置调度器
            if config:
                self._scheduler.configure(**config)
            
            # 添加事件监听器
            self._add_event_listeners()
            
            logger.info("创建调度器实例")
        
        if not self._scheduler.running:
            self._scheduler.start()
            self._is_running = True
            logger.info("调度器已启动")
            
            # 加载所有活跃的任务
            self._load_active_tasks()
        else:
            logger.warning("调度器已经在运行")
    
    def stop_scheduler(self):
        """停止调度器"""
        if self._scheduler and self._scheduler.running:
            self._scheduler.shutdown(wait=True)
            self._is_running = False
            logger.info("调度器已停止")
    
    def _add_event_listeners(self):
        """添加事件监听器"""
        self._scheduler.add_listener(
            self._job_executed_listener,
            EVENT_JOB_EXECUTED
        )
        self._scheduler.add_listener(
            self._job_error_listener,
            EVENT_JOB_ERROR
        )
        self._scheduler.add_listener(
            self._job_missed_listener,
            EVENT_JOB_MISSED
        )
    
    def _job_executed_listener(self, event):
        """任务执行成功监听器"""
        job_id = event.job_id
        logger.info(f"任务执行成功: {job_id}")
        self._update_task_run_time(job_id)
    
    def _job_error_listener(self, event):
        """任务执行失败监听器"""
        job_id = event.job_id
        exception = event.exception
        logger.error(f"任务执行失败: {job_id}, 错误: {exception}")
        self._update_task_run_time(job_id)
    
    def _job_missed_listener(self, event):
        """任务错过执行监听器"""
        job_id = event.job_id
        logger.warning(f"任务错过执行: {job_id}")
    
    def _update_task_run_time(self, job_id):
        """更新任务运行时间"""
        try:
            task = ScheduledTask.objects.filter(job_id=job_id).first()
            if task:
                task.last_run_time = timezone.now()
                # 计算下次运行时间
                job = self._scheduler.get_job(job_id)
                if job:
                    task.next_run_time = job.next_run_time
                task.save()
                
                # 更新统计信息
                metrics, created = TaskMetrics.objects.get_or_create(scheduled_task=task)
                metrics.update_metrics()
        except Exception as e:
            logger.error(f"更新任务运行时间失败: {e}")
    
    def _load_active_tasks(self):
        """加载所有活跃的任务"""
        active_tasks = ScheduledTask.objects.filter(is_active=True)
        loaded_count = 0
        
        for task in active_tasks:
            try:
                self.add_job_from_task(task)
                loaded_count += 1
            except Exception as e:
                logger.error(f"加载任务失败 {task.name}: {e}")
        
        logger.info(f"成功加载 {loaded_count} 个活跃任务")
    
    def add_job_from_task(self, task: ScheduledTask) -> bool:
        """从 ScheduledTask 模型添加任务到调度器"""
        if not self._scheduler:
            logger.error("调度器未初始化")
            return False
        
        try:
            # 导入任务函数
            module = importlib.import_module(task.module_path)
            func = getattr(module, task.function_name)
            
            # 创建触发器
            trigger = self._create_trigger(task.trigger_type, task.trigger_config)
            if not trigger:
                return False
            
            # 添加任务到调度器
            job = self._scheduler.add_job(
                func=self._job_wrapper,
                trigger=trigger,
                args=[task.id, func] + list(task.args),
                kwargs=task.kwargs,
                id=task.job_id,
                max_instances=task.max_instances,
                replace_existing=True,
                name=task.name
            )
            
            # 更新下次运行时间
            task.next_run_time = job.next_run_time
            task.save()
            
            logger.info(f"成功添加任务: {task.name} (ID: {task.job_id})")
            return True
            
        except Exception as e:
            logger.error(f"添加任务失败 {task.name}: {e}")
            return False
    
    def _job_wrapper(self, task_id: int, func, *args, **kwargs):
        """任务包装器，用于记录执行日志"""
        task = ScheduledTask.objects.get(id=task_id)
        
        # 创建执行日志
        log = TaskExecutionLog.objects.create(
            scheduled_task=task,
            status=TaskExecutionLog.StatusChoices.RUNNING
        )
        
        try:
            start_time = timezone.now()
            
            # 如果是异步函数，使用异步执行器
            if hasattr(func, '__code__') and func.__code__.co_flags & 0x80:  # 检查是否是协程
                task_id = self._task_executor.submit_task(f"scheduled_{task.id}", func, *args, **kwargs)
                # 等待异步任务完成（简化处理）
                result = f"异步任务已提交，任务ID: {task_id}"
            else:
                result = func(*args, **kwargs)
            
            end_time = timezone.now()
            execution_time = (end_time - start_time).total_seconds()
            
            # 更新日志状态
            log.status = TaskExecutionLog.StatusChoices.COMPLETED
            log.end_time = end_time
            log.execution_time = execution_time
            log.result = {'result': result} if result is not None else None
            log.save()
            
            logger.info(f"任务执行成功: {task.name}, 耗时: {execution_time:.2f}秒")
            
        except Exception as e:
            end_time = timezone.now()
            execution_time = (end_time - start_time).total_seconds()
            
            # 更新日志状态
            log.status = TaskExecutionLog.StatusChoices.FAILED
            log.end_time = end_time
            log.execution_time = execution_time
            log.error_message = str(e)
            log.save()
            
            logger.error(f"任务执行失败: {task.name}, 错误: {e}")
            raise
    
    def _create_trigger(self, trigger_type: str, config: Dict[str, Any]):
        """创建触发器"""
        try:
            if trigger_type == 'interval':
                return IntervalTrigger(**config)
            elif trigger_type == 'cron':
                return CronTrigger(**config)
            elif trigger_type == 'date':
                return DateTrigger(**config)
            else:
                logger.error(f"不支持的触发器类型: {trigger_type}")
                return None
        except Exception as e:
            logger.error(f"创建触发器失败: {e}")
            return None
    
    def remove_job(self, job_id: str) -> bool:
        """移除任务"""
        if not self._scheduler:
            return False
        
        try:
            self._scheduler.remove_job(job_id)
            logger.info(f"成功移除任务: {job_id}")
            return True
        except Exception as e:
            logger.error(f"移除任务失败 {job_id}: {e}")
            return False
    
    def pause_job(self, job_id: str) -> bool:
        """暂停任务"""
        if not self._scheduler:
            return False
        
        try:
            self._scheduler.pause_job(job_id)
            logger.info(f"成功暂停任务: {job_id}")
            return True
        except Exception as e:
            logger.error(f"暂停任务失败 {job_id}: {e}")
            return False
    
    def resume_job(self, job_id: str) -> bool:
        """恢复任务"""
        if not self._scheduler:
            return False
        
        try:
            self._scheduler.resume_job(job_id)
            logger.info(f"成功恢复任务: {job_id}")
            return True
        except Exception as e:
            logger.error(f"恢复任务失败 {job_id}: {e}")
            return False
    
    def modify_job(self, job_id: str, **changes) -> bool:
        """修改任务"""
        if not self._scheduler:
            return False
        
        try:
            self._scheduler.modify_job(job_id, **changes)
            logger.info(f"成功修改任务: {job_id}")
            return True
        except Exception as e:
            logger.error(f"修改任务失败 {job_id}: {e}")
            return False
    
    def get_jobs(self) -> List[Dict[str, Any]]:
        """获取所有任务状态"""
        if not self._scheduler:
            return []
        
        jobs = []
        for job in self._scheduler.get_jobs():
            jobs.append({
                'id': job.id,
                'name': job.name,
                'next_run_time': job.next_run_time,
                'trigger': str(job.trigger),
                'pending': job.pending,
            })
        return jobs
    
    def run_job_now(self, job_id: str) -> bool:
        """立即运行任务"""
        if not self._scheduler:
            return False
        
        try:
            job = self._scheduler.get_job(job_id)
            if job:
                job.modify(next_run_time=timezone.now())
                logger.info(f"任务已设置为立即运行: {job_id}")
                return True
            else:
                logger.error(f"任务不存在: {job_id}")
                return False
        except Exception as e:
            logger.error(f"立即运行任务失败 {job_id}: {e}")
            return False
    
    @property
    def is_running(self) -> bool:
        """检查调度器是否运行中"""
        return self._is_running and self._scheduler and self._scheduler.running
    
    def get_scheduler_status(self) -> Dict[str, Any]:
        """获取调度器状态"""
        if not self._scheduler:
            return {
                'running': False,
                'job_count': 0,
                'next_run_time': None
            }
        
        jobs = self._scheduler.get_jobs()
        next_run_times = [job.next_run_time for job in jobs if job.next_run_time]
        
        return {
            'running': self.is_running,
            'job_count': len(jobs),
            'next_run_time': min(next_run_times) if next_run_times else None
        }